﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using BITS.PreCompiler.CSharp.Syntax;
using BITS.PreCompiler.CSharp.Visitors;
using Roslyn.Compilers.CSharp;

namespace BITS.PreCompiler.CSharp
{
    public partial class Unit : IVisitable<Unit>
        , IEnumerable<Module>
        , IEnumerable<IEnumerable<Module>>
    {
        # region • Fields & Properties •

        /// <summary>
        /// Holds the output name.
        /// </summary>
        private String outputName;

        /// <summary>
        /// Gets or sets the name of the output.
        /// </summary>
        /// <value>
        /// The name of the output.
        /// </value>
        public String OutputName
        {
            get
            {
                return this.outputName
                    ?? (this.outputName = Guid.NewGuid().ToString());
            }
            set { this.outputName = value; }
        }

        public String OutputNameWithoutExtension
        {
            get
            {
                return Path.GetFileNameWithoutExtension(
                    this.OutputName);
            }
        }

        public String OutputNameExtension
        {
            get
            {
                return Path.GetExtension(
                    this.OutputName);
            }
        }

        /// <summary>
        /// Holds the modules.
        /// </summary>
        private readonly List<Module> modules
            = new List<Module>();

        /// <summary>
        /// Gets the modules.
        /// </summary>
        public IEnumerable<Module> Modules
        {
            get { return this.modules; }
        }

        # endregion Fields & Properties

        # region • Ctor's •

        /// <summary>
        /// Initializes a new instance of the <see cref="Module"/> class.
        /// </summary>
        /// <param name="outputName">Name of the output.</param>
        /// <param name="modules">Additional modules.</param>
        public Unit(String outputName = null, params Module[] modules)
        {
            this.OutputName = outputName;
            this.Add(modules);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Module"/> class.
        /// </summary>
        /// <param name="unit">The unit.</param>
        public Unit(Unit unit)
        {
            this.OutputName = unit.OutputName;
            this.Add(unit.Modules);
        }

        # endregion Ctor's

        # region • Methods •

        /// <summary>
        /// Adds the specified module.
        /// </summary>
        /// <param name="module">The module.</param>
        public Unit Add(Module @module)
        {
            if (@module == null)
                return this;

            this.modules.Add(@module);

            return this;
        }

        /// <summary>
        /// Adds the specified module list.
        /// </summary>
        /// <param name="list">The module list.</param>
        public Unit Add(IEnumerable<Module> @list)
        {
            if (@list == null)
                return this;

            foreach (var module in @list)
                this.Add(module);

            return this;
        }

        /// <summary>
        /// Clones this instance.
        /// </summary>
        /// <returns></returns>
        public Unit Clone()
        {
            return new Unit(this);
        }

        /// <summary>
        /// Gets the syntax trees.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<SyntaxTree> GetSyntaxTrees()
        {
            return this.Modules.Select(x => x.GetSyntaxTree());
        }

        /// <summary>
        /// Gets the syntax node.
        /// </summary>
        /// <returns></returns>
        public SyntaxNode GetSyntaxNode()
        {
            return this.GetSyntaxTree().GetRoot();
        }

        /// <summary>
        /// Gets the semantic model.
        /// </summary>
        /// <returns></returns>
        public SemanticModel GetSemanticModel()
        {
            var compilation = this.GetCompilation();

            return compilation.GetSemanticModel(
                compilation.SyntaxTrees.Single());
        }

        public Unit Merge(Unit @unit)
        {
            this.Add(@unit.Modules);

            return this;
        }

        /// <summary>
        /// Visitor pattern.
        /// </summary>
        /// <param name="visitor"></param>
        /// <returns></returns>
        public Unit Accept(ISyntaxVisitor<Unit> visitor)
        {
            return visitor.Visit(this);
        }

        public override String ToString()
        {
            var code = new StringBuilder();

            foreach (var module in this.Modules)
                code.AppendLine(module.ToString());

            return code.ToString();
        }

        # region • Methods - IEnumerable •

        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
        /// </returns>
        IEnumerator<Module> IEnumerable<Module>.GetEnumerator()
        {
            return this.Modules.GetEnumerator();
        }

        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> 
        /// that can be used to iterate through the collection.
        /// </returns>
        IEnumerator<IEnumerable<Module>> IEnumerable<IEnumerable<Module>>.GetEnumerator()
        {
            var @this = this as IEnumerable<Module>;

            yield return @this;
        }

        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
        /// </returns>
        /// <filterpriority>2</filterpriority>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.Modules.GetEnumerator();
        }

        # endregion Methods - IEnumerable

        # endregion Methods
    }
}
